package org.xx.armory.spring5.vfs2;

import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
import org.apache.commons.vfs2.provider.FileProvider;
import org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;

/**
 * 用于构造 {@link FileSystemManager} 的工厂类。
 */
public class FileSystemManagerFactory
        implements FactoryBean<FileSystemManager>, InitializingBean, DisposableBean {
    private final Logger logger = LoggerFactory.getLogger(FileSystemManagerFactory.class);
    private final AtomicBoolean init;
    private final Map<String, FileProvider> providers;
    private DefaultFileSystemManager fileSystemManager;

    public FileSystemManagerFactory() {
        this.init = new AtomicBoolean(false);
        this.providers = new LinkedHashMap<>();
    }

    public Map<String, FileProvider> getProviders() {
        return Collections.unmodifiableMap(this.providers);
    }

    public void setProviders(
            Map<String, FileProvider> providers
    ) {
        if (providers != null) {
            this.providers.putAll(providers);
        }
    }

    /**
     * 使用指定的参数创建一个文件系统管理器。
     *
     * @param providers
     *         文件系统提供程序集合。
     * @throws IOException
     *         向文件系统管理器注册文件系统提供程序时出错。
     */
    private DefaultFileSystemManager buildFileSystemManager(
            Map<String, FileProvider> providers
    )
            throws IOException {
        final DefaultFileSystemManager result = new DefaultFileSystemManager();

        logger.trace("Building file-system manager from providers(size=" + this.providers.size() + ")");

        FileProvider firstProvider = null;
        for (final Map.Entry<String, FileProvider> entry : providers.entrySet()) {
            final String schema = trimToEmpty(entry.getKey());
            final FileProvider provider = entry.getValue();
            result.addProvider(schema, provider);
            logger.trace("  schema: {}, provider: {}", schema, provider);
            if (firstProvider == null) {
                firstProvider = provider;
            }
        }

        if (firstProvider != null) {
            result.setDefaultProvider(firstProvider);
            logger.trace("  default provider: {}", firstProvider);
        }

        logger.trace("Initializing file-system manager");
        result.init();
        logger.trace("Initialized file-system manager");

        return result;
    }

    @Override
    public FileSystemManager getObject()
            throws Exception {
        return this.fileSystemManager;
    }

    @Override
    public Class<?> getObjectType() {
        return FileSystemManager.class;
    }

    @Override
    public void afterPropertiesSet()
            throws Exception {
        if (!this.init.get()) {
            if (this.providers.isEmpty()) {
                this.providers.put("", new DefaultLocalFileProvider());
            }
            this.fileSystemManager = buildFileSystemManager(this.providers);
            this.init.set(true);
        }
    }

    @Override
    public void destroy()
            throws Exception {
        if (this.init.get()) {
            if (this.fileSystemManager != null) {
                this.fileSystemManager.close();
                this.fileSystemManager = null;
            }

            this.init.set(false);
        }
    }
}

